/*  SSTablePrintView.m
 *  MESLib
 * 
 * Copyright 2003-2009 Starline Software Ltd
 * 
 * This file is part of MESLib.
 * 
 * MESLib is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * MESLib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with MESLib. If not, see <http://www.gnu.org/licenses/>.
 */
//  Created by Tobias Haughton on Tue Jul 08 2003.
//  Copyright (c) 2003 2003 Starline Software Ltd. All rights reserved.
//
// the SSTablePrintView class takes an arry of data and an array of columns and uses this to build a table like structure sutable for printing


#import "SSTablePrintView.h"


@implementation SSTablePrintView

- (id)initWithDataArray:(NSMutableArray *)data columnArray:(NSArray *)columns printInfo:(NSPrintInfo *)pi
{
   //columnArray must contain nsdictionaries that have
      //an NSString for the key "title"
      //an NSNumber for the key "percentOfPageWidth"
      //an NSString for the key "dataAccessor"

   //the data array can contain any type of objects but all must respond to the dataAccessors contained in the columnArray


   
   //validate the input
   if(![self validateInputWithDataArray:data columns:columns]) {
      return nil;
   }
      
   rowArray = [[NSMutableArray alloc] init];
   printInfo = pi;
   [self createRowsWithColumnArray:columns dataArray:data];
   [self setNumberOfPages];
   lastPageMaxY = 0.0;
   if (self = [super initWithFrame:[self minimumFrameToFitContents]]) {

   }

   return self;
}

- (BOOL)validateInputWithDataArray:(NSArray *)data columns:(NSArray *)columns
{
   NSEnumerator *colEnumerator = [columns objectEnumerator];
   NSEnumerator *dataEnumerator = [data objectEnumerator];
   id colObject;
   id dataObject;

   while (colObject = [colEnumerator nextObject]) {
      //loop through the column array and validate the data
      if([colObject respondsToSelector:@selector(objectForKey:)]) {
         if(![colObject objectForKey:@"title"]) {
            return NO;
         }

         if(![colObject objectForKey:@"percentOfPageWidth"]) {
            return NO;
         }

         if(![colObject objectForKey:@"dataAccessor"]) {
            return NO;
         }
      } else {
         return NO;
      }
   }

   //we have got this far so the columns are fine what about our data
   while (dataObject = [dataEnumerator nextObject]) {
      colEnumerator = [columns objectEnumerator];
      //loop through and see if this object responds to all of the passed accessors
      while(colObject = [colEnumerator nextObject]) {
      	 SEL theMethodSelector = NSSelectorFromString([colObject objectForKey:@"dataAccessor"]);
         if(![dataObject respondsToSelector:theMethodSelector]) {
            //this object doesn't respond to the selector so we can't continue
            return NO;
         }
      }
   }

   //everything looks valid
   return YES;
}

- (void)drawRect:(NSRect)rect {
   [[NSColor whiteColor] set];
   [NSBezierPath fillRect:[self bounds]];
   
    NSEnumerator *enumerator = [rowArray objectEnumerator];
    id anObject;

    while (anObject = [enumerator nextObject]) {
       if(NSIntersectsRect([anObject frame], rect)) {
          [anObject drawInView:self];
       }
    }
}

- (void)createRowsWithColumnArray:(NSArray *)columnArray dataArray:(NSArray *)dataArray
{
   NSEnumerator *columnEnumerator = [columnArray objectEnumerator];
   NSEnumerator *rowEnumerator = [dataArray objectEnumerator];
   id columnObject;
   id rowObject;
   NSMutableArray *headerCells = [NSMutableArray array];
   NSMutableArray *widths = [NSMutableArray array];
   float yPosition = 0.0;

   //create the header and set the column widths
   while (columnObject = [columnEnumerator nextObject]) {
      NSTextFieldCell *cell = [[NSTextFieldCell alloc] initTextCell:[columnObject objectForKey:@"title"]];
      [cell setFont:[NSFont fontWithName:@"Helvetica-Bold" size:12]];
      [cell setDrawsBackground:YES];
      [headerCells addObject:cell];

      [widths addObject:[NSNumber numberWithFloat:(([self paperSize].width * [[columnObject objectForKey:@"percentOfPageWidth"] floatValue]) - 1)]];
   }
   
   SSTablePrintViewRow *header = [[[SSTablePrintViewRow alloc] initWithColumns:headerCells columnWidths:widths origin:NSMakePoint(0, yPosition)] autorelease];
   if(header != nil) {
      [rowArray addObject:header];
      yPosition = NSMaxY([header frame]);
   }

   //loop through the data array and build a row for each
   while (rowObject = [rowEnumerator nextObject]) {
      NSMutableArray *rowCells = [NSMutableArray array];
      columnEnumerator = [columnArray objectEnumerator];
      while (columnObject = [columnEnumerator nextObject]) {
         SEL theMethodSelector = NSSelectorFromString([columnObject objectForKey:@"dataAccessor"]); //get a selector from key so that we can call the correct method from the object
         NSTextFieldCell *cell = [[NSTextFieldCell alloc] initTextCell:[rowObject performSelector:theMethodSelector]]; //get the value for this cell from the data object
         [cell setFont:[NSFont fontWithName:@"Helvetica" size:12]];
         [cell setDrawsBackground:YES];
         [rowCells addObject:cell];
      }

      SSTablePrintViewRow *row = [[[SSTablePrintViewRow alloc] initWithColumns:rowCells columnWidths:widths origin:NSMakePoint(0, yPosition)] autorelease];

      if(row != nil) {
         [rowArray addObject:row];
         yPosition = NSMaxY([row frame]);
      }
   }
}

- (NSRect)minimumFrameToFitContents
{
   return NSMakeRect(0, 0, [self paperSize].width, NSMaxY([[rowArray lastObject] frame]));
}

- (void)setNumberOfPages
{
   //operator will be rounded up
   pages = ceil(NSHeight([self minimumFrameToFitContents]) / [self paperSize].height);
}

- (NSSize)paperSize
{
   NSSize paperSize = [printInfo paperSize];
   paperSize.width -= ([printInfo leftMargin] + [printInfo rightMargin]);
   paperSize.height -= ([printInfo topMargin] + [printInfo bottomMargin]);

   return paperSize;
}

- (BOOL)knowsPageRange:(NSRange *)rptr
{
   rptr->location = 1;
   rptr->length = pages;
   return YES;
}

- (NSRect)rectForPage:(int)pageNum
{
   NSRect theResult;
   theResult.size.width = [self bounds].size.width;
   theResult.size.height = [self heightForPageWithYOrigin:lastPageMaxY];
   theResult.origin.x = [self bounds].origin.x;
   theResult.origin.y = lastPageMaxY;

   lastPageMaxY += theResult.size.height;
   
   return theResult;
}

- (float)heightForPageWithYOrigin:(float)yOrigin
{
   float proposedBottom = yOrigin + [self paperSize].height;
   int i = 0;

   //loop through the rows and get a height to won't cut any of them off
   while (i < [rowArray count]) {
      if(NSMaxY([[rowArray objectAtIndex:i] frame]) == proposedBottom) {
         //this row finishes at the exact position as proposedBottom so use the normal height
         return [self paperSize].height;
      } else if([[rowArray objectAtIndex:i] containsPoint:NSMakePoint(1, proposedBottom)]) {
         //this row contains the point and thus will be cut off if we use the normal paper height
         return (NSMaxY([[rowArray objectAtIndex:(i - 1)] frame]) - yOrigin);
      }
      i++;
   }

   return [self paperSize].height;
}

//*************************
- (BOOL)isFlipped
{
   //changes the coordinate system so 0,0 is in the top left
   return YES;
}

- (BOOL)isOpaque
{
   // State that the view is opaque for optimization purposes.
   return YES;
}
//*************************

- (void)dealloc
{
   [rowArray release];
   [super dealloc];
}

@end
